#ifndef CONVOLUTION_HH_
# define CONVOLUTION_HH_

# include "tbb/task_scheduler_init.h"
# include "tbb/blocked_range2d.h"
# include <SDL/SDL.h>
# include "common.hh"

class Convolution
{
  Uint32	*img_in, *img_out;
  int		**mat;

  //img_out must be init.
 public:
  Convolution (Uint32		*img_in_,
	       Uint32		*img_out_,
	       int		**mat_)
    : img_in(img_in_),
      img_out(img_out_),
      mat(mat_)
    {
    }

  void operator() (const tbb::blocked_range2d<int>& r) const
  {
    for (int i = r.rows().begin(); i!= r.rows().end(); ++i)
      {
	for (int j = r.cols().begin(); j!= r.cols().end(); ++j)
	  {
	    Uint32 coul = img_in[i * WIDTH + j];

	    int sum_red, sum_green, sum_blue;
	    Uint8 red, green, blue;

	    SDL_GetRGB (coul, global_screen->format, &red, &green, &blue);

	    sum_red = mat[1][1] * red;
	    sum_green = mat[1][1] * green;
	    sum_blue = mat[1][1] * blue;

	    if (i > 0)
	      {
		if (j > 0)
		  {
		    //Upper left corner
		    Uint32 ul = img_in[(i - 1) * WIDTH + j - 1];
		    SDL_GetRGB (ul, global_screen->format, &red, &green, &blue);
		    sum_red += mat[0][0] * red;
		    sum_blue += mat[0][0] * blue;
		    sum_green += mat[0][0] * green;
		  }
		//middle left corner
		Uint32 ml = img_in[(i - 1) * WIDTH + j];
		SDL_GetRGB (ml, global_screen->format, &red, &green, &blue);
		sum_red += mat[0][1] * red;
		sum_blue += mat[0][1] * blue;
		sum_green += mat[0][1] * green;
		if (j < WIDTH - 1)
		  {
		    // bottom left corner
		    Uint32 ur = img_in[(i - 1) * WIDTH + j + 1];
		    SDL_GetRGB (ur, global_screen->format, &red, &green, &blue);
		    sum_red += mat[0][2] * red;
		    sum_blue += mat[0][2] * blue;
		    sum_green += mat[0][2] * green;
		  }
	      }
	    if (j > 0)
	      {
		//Middle up  corner
		Uint32 mu = img_in[i * WIDTH + j - 1];
		SDL_GetRGB (mu, global_screen->format, &red, &green, &blue);
		sum_red += mat[1][0] * red;
		sum_blue += mat[1][0] * blue;
		sum_green += mat[1][0] * green;
	      }
	    if (j < WIDTH - 1)
	      {
		//Middle bottom  corner
		Uint32 mb = img_in[i * WIDTH + j + 1];
		SDL_GetRGB (mb, global_screen->format, &red, &green, &blue);
		sum_red += mat[1][2] * red;
		sum_blue += mat[1][2] * blue;
		sum_green += mat[1][2] * green;
	      }
	    if (i < HEIGHT - 1)
	      {
		if (j > 0)
		  {
		    //Upper right corner
		    Uint32 ur = img_in[(i + 1) * WIDTH + j - 1];
		    SDL_GetRGB (ur, global_screen->format, &red, &green, &blue);
		    sum_red += mat[2][0] * red;
		    sum_blue += mat[2][0] * blue;
		    sum_green += mat[2][0] * green;
		  }
		//middle right corner
		Uint32 mr = img_in[(i + 1) * WIDTH + j];
		SDL_GetRGB (mr, global_screen->format, &red, &green, &blue);
		sum_red += mat[2][1] * red;
		sum_blue += mat[2][1] * blue;
		sum_green += mat[2][1] * green;
		if (j < WIDTH - 1)
		  {
		    // bottom right right corner
		    Uint32 br = img_in[(i + 1) * WIDTH + j + 1];
		    SDL_GetRGB (br, global_screen->format, &red, &green, &blue);
		    sum_red += mat[2][2] * red;
		    sum_blue += mat[2][2] * blue;
		    sum_green += mat[2][2] * green;
		  }
	      }
	    if (sum_red > 255)
	      sum_red = 255;
	    if (sum_blue > 255)
	      sum_blue = 255;
	    if (sum_green > 255)
	      sum_green = 255;
	    if (sum_red < 0)
	      sum_red = 0;
	    if (sum_blue < 0)
	      sum_blue = 0;
	    if (sum_green < 0)
	      sum_green = 0;

	    img_out[i * WIDTH + j] = SDL_MapRGB (global_screen->format, red, green, blue);
	  }
      }
    if (img == global_screen->pixels)
      {
	tbb::spin_mutex::scoped_lock lock;

	lock.acquire(global_screen_mutex);

	SDL_UpdateRect(global_screen,
		       r.cols().begin(),
		       r.rows().begin(),
		       r.cols().end() - r.cols().begin(),
		       r.rows().end() - r.rows().begin());

	lock.release();
      }
  }

  Uint32*	get_img_out()
  {
    return img_out;
  }
};

#endif /* !CONVOLUTION_HH_ */
